home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / PNL Libraries / TCPStuff.p < prev    next >
Encoding:
Text File  |  1994-10-03  |  35.1 KB  |  1,247 lines  |  [TEXT/PJMM]

  1. unit TCPStuff;
  2.  
  3. { TCPStuff © Peter Lewis, Oct 1991 }
  4. { This source is Freeware }
  5.  
  6. interface
  7.  
  8.     uses
  9.         TCPTypes;
  10.  
  11.     const
  12.         Minimum_TCPBufferSize = 4096;
  13.         Default_TCPBufferSize = longInt(6) * 1024;
  14.         Maximum_TCPBufferSize = 30000;
  15.     { Amount of space to allocate for each TCP connection }
  16.         INCOMINGBUFSIZE = 100;    { Incoming buffer size, used for buffering ReceiveUpTo. }
  17.         control_block_max = 260;
  18.         tooManyControlBlocks = -23098;
  19.         Default_UDPBufferSize = 4096;
  20.  
  21.     type
  22.         OSErrPtr = ^OSErr;
  23.  
  24. { TCP connection description: }
  25.         TCPConnectionType = record
  26.                 magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  27.                 stream: StreamPtr;
  28.                 closedone: boolean;
  29.                 laststate: integer;
  30.                 asends, asendcompletes: longInt;
  31.                 closeuserptr: OSErrPtr;
  32.                 incomingPtr: Ptr;                                { Pointer into inBuf of next byte to read. }
  33.                 incomingSize: longInt;                        { Number of bytes left in inBuf. }
  34.                 buffer: ptr;        { connection buffer. }
  35.                 inBuf: array[1..INCOMINGBUFSIZE] of SignedByte;    {Input buffer. }
  36.             end;
  37.         TCPConnectionPtr = ^TCPConnectionType;
  38.  
  39.         MyControlBlock = record
  40.                 tcp: TCPControlBlock;
  41.                 inuse: boolean;
  42.                 userptr: OSErrPtr;
  43.                 proc: procPtr;
  44.                 tcpc: TCPConnectionPtr;
  45.             end;
  46.         MyControlBlockPtr = ^MyControlBlock;
  47.  
  48.         TCPXControlBlock = record
  49.                 completion: ProcPtr;
  50.                 pb: TCPControlBlock;
  51.             end;
  52.         TCPXControlBlockPtr = ^TCPXControlBlock;
  53.  
  54.         TCPStateType = (T_WaitingForOpen, T_Closed, T_Listening, T_Opening, T_Established,{}
  55.             T_Closing, T_PleaseClose, T_Unknown);
  56.  
  57.     type
  58.         UDPConnectionRecord = record
  59.                 magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  60.                 stream: StreamPtr;
  61.                 outstanding: integer;
  62.             end;
  63.         UDPConnectionPtr = ^UDPConnectionRecord;
  64.  
  65.     type
  66.         DNRCompletionProcPtr = ProcPtr;
  67. { procedure DNRCompletionProc(drp:DNRRecordPtr); }
  68.         DNRRecord = record
  69. { Generally you only need to look at the first three of these }
  70.                 ioResult: OSErr;
  71.                 name: Str255;
  72.                 addr: longInt;
  73.                 completion: DNRCompletionProcPtr;
  74.                 case integer of
  75.                     1: (
  76.                             hi: hostInfo;
  77.                     );
  78.                     2: (
  79.                             hmx: hmxInfoRec;
  80.                     );
  81.                     3: (
  82.                             cacherec: cacheEntryRecord;
  83.                     );
  84.             end;
  85.         DNRRecordPtr = ^DNRRecord;
  86.  
  87.     var
  88.         icmp_sent_out, icmp_got_back: longInt;
  89.  
  90.     function C2PStr (s: stringPtr): stringPtr;
  91.     procedure SanitizeHostName (var s: str255);
  92.  
  93.     function TCPInit: OSErr;
  94.     procedure TCPFinish;
  95.  
  96.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  97.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  98.     function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: streamPtr): OSErr;
  99.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  100.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  101.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  102.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  103.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  104.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  105.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  106.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  107.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  108. { Use EITHER RawReceive, or the other Receives.  Don't combine them for one stream! }
  109.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  110.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  111.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  112.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  113.                                     var gottermchar: boolean): OSErr;
  114.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean): OSErr;
  115.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  116.  
  117.     function UDPCreate (var connection: UDPConnectionPtr; buffer_size: longInt; var localport: integer): OSErr;
  118.     function UDPRead (connection: UDPConnectionPtr; timeout: integer; var remoteIP: longInt; var remoteport: integer;{}
  119.                                     var datap: ptr; var datalen: integer): OSErr;
  120.     function UDPReturnBuffer (connection: UDPConnectionPtr; datap: ptr): OSErr;
  121.     function UDPDatagramsAvailable (connection: UDPConnectionPtr): integer;
  122.     function UDPWrite (connection: UDPConnectionPtr; remoteIP: longInt; remoteport: integer;{}
  123.                                     datap: ptr; datalen: integer; checksum: boolean): OSErr;
  124.     function UDPRelease (var connection: UDPConnectionPtr): OSErr;
  125.     function UDPMTU (remoteIP: longInt; var mtu: integer): OSErr;
  126.  
  127.     function IPGetMyIPAddr (var myIP: longInt): OSErr;
  128.     function IPSendICMPEcho (remotehost: ipAddr; timeout: integer; datap: ptr; datalen: integer; complete: ProcPtr; userdata: univ ptr; extradata: univ ptr): OSErr;
  129. {procedure ICMPCompletion (cbp: IPControlBlockPtr; userdata: ptr;extradata:ptr);}
  130.  
  131.     procedure DNRNameToAddr (name: Str255; drp: DNRRecordPtr; completion: DNRCompletionProcPtr);
  132.     procedure DNRAddrToName (addr: longInt; drp: DNRRecordPtr; completion: DNRCompletionProcPtr);
  133.  
  134.     procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  135.     procedure UDPZeroCB (var cb: UDPControlBlock; stream: StreamPtr; call: integer);
  136.     procedure TCPControlAsync (pbp: TCPControlBlockPtr; comp: ProcPtr);
  137. { pbp MUST be a ptr to an XTCPControlBlock }
  138.  
  139. implementation
  140.  
  141.     uses
  142. {$IFC undefined THINK_Pascal}
  143.         Memory, Errors, Devices, Events, 
  144. {$ENDC}
  145.         DNR;
  146.  
  147.     const
  148.         MAGICNUMBER = 'TMGK';    { Unique value used to trap illegal connection IDs. }
  149.         UDPMagic = 'UDPM';
  150.         UDPBad = '????';
  151.         dispose_block_max = 100;
  152.  
  153.     type
  154.         MyControlBlockArray = array[1..control_block_max] of MyControlBlockPtr;
  155.  
  156.     type
  157.         stackframe = packed record
  158.                 frameptr: ptr;
  159.                 returnptr: ptr;
  160.                 paramblockptr: ptr;
  161.             end;
  162.         stackframeptr = ^stackframe;
  163.  
  164.     var
  165.         driver_refnum: integer;
  166.         controlblocks: MyControlBlockArray;
  167.         disposeblocks: array[1..dispose_block_max] of ptr;
  168.  
  169.     const
  170.         max_ICMPDataArray = 100;
  171.     type
  172.         ICMPData = record
  173.                 complete: ProcPtr;
  174.                 userdata: ptr;
  175.                 extradata: ptr;
  176.             end;
  177.         ICMPDataArray = array[1..max_ICMPDataArray] of ICMPData;
  178.     var
  179.         icmp_data_array: ICMPDataArray;
  180.  
  181. {$PUSH}
  182. {$D-}
  183. {$R-}
  184.     procedure SanitizeHostName (var s: str255);
  185.         var
  186.             dummysp: stringPtr;
  187.     begin
  188.         dummysp := C2PStr(@s);
  189.         if s[Length(s)] = '.' then
  190.             s[0] := chr(Length(s) - 1);
  191.     end;
  192. {$POP}
  193.  
  194.     function GetStackFrame: stackframeptr;
  195.     inline
  196.         $2E8E;
  197.  
  198.     procedure CallIOCompletion (cbp: MyControlBlockPtr; addr: procPtr);
  199.     inline
  200.         $205F, $4E90;
  201.  
  202.     procedure CallTCPCompletion (cbp: TCPControlBlockPtr; addr: procPtr);
  203.     inline
  204.         $205F, $4E90;
  205.  
  206. {$PUSH}
  207. {$D-}
  208.  
  209.     procedure TCPPreCompletion; { All C functions look like pascal paramterless procedures from the procs point of view }
  210.         var
  211.             prp: TCPXControlBlockPtr;
  212.             pbp: TCPControlBlockPtr;
  213.     begin
  214.         pbp := TCPControlBlockPtr(GetStackFrame^.paramblockptr);
  215.         prp := TCPXControlBlockPtr(ord(pbp) - 4);
  216.         if prp^.completion <> nil then begin
  217.             CallTCPCompletion(pbp, prp^.completion);
  218.         end;
  219.     end;
  220.  
  221.     procedure TCPControlAsync (pbp: TCPControlBlockPtr; comp: ProcPtr);
  222.         var
  223.             err: OSErr;
  224.             prp: TCPXControlBlockPtr;
  225.     begin
  226.         prp := TCPXControlBlockPtr(ord(pbp) - 4);
  227.         prp^.completion := comp;
  228.         pbp^.ioCompletion := @TCPPreCompletion;
  229.         err := PBControlAsync(ParmBlkPtr(pbp));
  230.         if err <> noErr then begin
  231.             pbp^.ioResult := err;
  232.             if prp^.completion <> nil then begin
  233.                 CallTCPCompletion(pbp, prp^.completion);
  234.             end;
  235.         end;
  236.     end;
  237.  
  238.     procedure IOCompletionPascal (cbp: MyControlBlockPtr);
  239.     begin
  240.         with cbp^ do begin
  241.             if userptr <> nil then begin
  242.                 userptr^ := cbp^.tcp.ioResult;
  243.             end;
  244.             inuse := false;
  245.             if proc <> nil then begin
  246.                 CallIOCompletion(cbp, proc);
  247.             end;
  248.         end;
  249.     end;
  250.  
  251. {$IFC undefined THINK_Pascal}
  252.  
  253.     procedure IOCompletion;
  254.         asm;
  255.     begin
  256.         move.l 4 (sp), a0
  257.         move.l a0,-(sp)
  258.         jsr IOCompletionPascal
  259.         rts
  260. {        move.l 4 (sp), a0}
  261. {        move.l a0,-(sp)}
  262. {        jsr IOCompletionPascal}
  263. {        rts}
  264.     end;
  265.  
  266. {$ELSEC}
  267.  
  268.     procedure IOCompletion; { All C functions look like pascal paramterless procedures from the procs point of view }
  269.     begin
  270.         IOCompletionPascal(MyControlBlockPtr(GetStackFrame^.paramblockptr));
  271.     end;
  272.  
  273. {$ENDC}
  274.  
  275.     procedure ZotBlocks;
  276.         var
  277.             i: integer;
  278.     begin
  279.         for i := 1 to dispose_block_max do begin
  280.             if disposeblocks[i] <> nil then begin
  281.                 DisposePtr(disposeblocks[i]);
  282.                 disposeblocks[i] := nil;
  283.             end;
  284.         end;
  285.     end;
  286.  
  287.     procedure AddBlock (p: univ ptr);
  288. { Called at interupt level }
  289. { Must work even while ZotBlocks is in progress }
  290.         var
  291.             i: integer;
  292.     begin
  293.         for i := 1 to dispose_block_max do begin
  294.             if disposeblocks[i] = nil then begin
  295.                 disposeblocks[i] := p;
  296.                 leave;
  297.             end;
  298.         end;
  299.     end;
  300.  
  301.     procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  302.     { Zero out the control block parameters. }
  303.         var
  304.             i: integer;
  305.             p: longInt;
  306.     begin
  307.         for p := longInt(@cb) to longInt(@cb) + SizeOf(TCPControlBlock) - 1 do
  308.             ptr(p)^ := 0;
  309.         cb.tcpStream := stream;
  310.         cb.ioCRefNum := driver_refnum;
  311.         cb.csCode := call;
  312.     end;
  313.  
  314.     function GetCB (var cbp: MyControlBlockPtr; tcpc: TCPConnectionPtr; call: integer; userptr: OSErrPtr; proc: procptr): OSErr;
  315. { NOTE: Must not move memory if there is a free block available (ie, during a Completion call) }
  316.         var
  317.             i: integer;
  318.     begin
  319.         i := 1;
  320.         while (i < control_block_max) & (controlblocks[i] <> nil) & controlblocks[i]^.inuse do
  321.             i := i + 1;
  322.         cbp := controlblocks[i];
  323.         if cbp = nil then begin
  324.             cbp := MyControlBlockPtr(NewPtr(SizeOf(MyControlBlock)));
  325.             if cbp <> nil then begin
  326.                 cbp^.inuse := false;
  327.                 controlblocks[i] := cbp;
  328.             end;
  329.         end;
  330.         if (cbp <> nil) & not cbp^.inuse then begin
  331.             ZeroCB(cbp^.tcp, tcpc^.stream, call);
  332.             cbp^.tcp.ioCompletion := @IOCompletion;
  333.             cbp^.inuse := true;
  334.             cbp^.userptr := userptr;
  335.             cbp^.tcpc := tcpc;
  336.             cbp^.proc := proc;
  337.             if userptr <> nil then
  338.                 userptr^ := inprogress;
  339.             GetCB := noErr;
  340.         end
  341.         else begin
  342.             cbp := nil;
  343.             GetCB := memFullErr;
  344.         end;
  345.     end;
  346.  
  347.     procedure FreeCB (var cbp: MyControlBlockPtr);
  348.     begin
  349.         if cbp <> nil then
  350.             cbp^.inuse := false;
  351.         cbp := nil;
  352.     end;
  353. {$POP}
  354.  
  355. {$S Init}
  356.     function TCPInit: OSErr;
  357.         var
  358.             oe: OSErr;
  359.             i: integer;
  360.     begin
  361.         oe := OpenDriver('.IPP', driver_refnum);
  362.         for i := 1 to control_block_max do
  363.             controlblocks[i] := nil;
  364.         for i := 1 to max_ICMPDataArray do
  365.             icmp_data_array[i].complete := nil;
  366.         TCPInit := oe;
  367.     end;
  368.  
  369. {$S Term}
  370.     procedure TCPFinish;
  371.         var
  372.             i: integer;
  373.     begin
  374.         for i := 1 to control_block_max do
  375.             if controlblocks[i] <> nil then begin
  376.                 DisposPtr(ptr(controlblocks[i]));
  377.                 controlblocks[i] := nil;
  378.             end;
  379.     end;
  380.  
  381. {$S}
  382.     procedure DestroyConnection (var connection: TCPConnectionPtr);
  383.     begin
  384.         connection^.magic := '????';
  385.         if connection^.buffer <> nil then
  386.             DisposPtr(ptr(connection^.buffer));
  387.         DisposPtr(Ptr(connection));
  388.         connection := nil;
  389.     end;
  390.  
  391.     function ValidateConnection (connection: TCPConnectionPtr): OSErr;
  392.     begin
  393.         if (connection = nil) | (connection^.magic <> MAGICNUMBER) then begin
  394.             ValidateConnection := connectionDoesntExistErr;
  395.         end
  396.         else begin
  397.             ValidateConnection := noErr;
  398.         end;
  399.     end;
  400.  
  401. {$PUSH}
  402. {$D-}
  403.     function MyPBControlAsync (var cbp: MyControlBlockPtr): OSErr;
  404.         var
  405.             oe: OSErr;
  406.     begin
  407.         oe := PBControlAsync(ParmBlkPtr(cbp));
  408.         if oe <> noErr then begin
  409.             FreeCB(cbp);
  410.         end;
  411.         MyPBControlAsync := oe;
  412.     end;
  413. {$POP}
  414.  
  415.     procedure SetUserPtr (userptr: OSErrPtr; oe: OSErr);
  416.     begin
  417.         if userptr <> nil then begin
  418.             if oe <> noErr then
  419.                 userptr^ := oe;
  420.         end;
  421.     end;
  422.  
  423.     function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: streamPtr): OSErr;
  424.         var
  425.             oe: OSErr;
  426.     begin
  427.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  428.         if connection = nil then
  429.             oe := memFullErr
  430.         else begin
  431.             oe := noErr;
  432.             with connection^ do begin
  433.                 buffer := nil;
  434.                 magic := MAGICNUMBER;
  435.                 asends := 0;
  436.                 asendcompletes := 0;
  437.                 closedone := false;
  438.                 incomingSize := 0;
  439.                 stream := strm;
  440.             end;
  441.         end;
  442.         if (oe <> noErr) and (connection <> nil) then
  443.             DestroyConnection(connection);
  444.         TCPCreateConnectionForStream := oe;
  445.     end;
  446.  
  447.     function CreateStream (var connection: TCPConnectionPtr; buffersize: longInt): OSErr;
  448.         var
  449.             oe: OSErr;
  450.             cb: TCPControlBlock;
  451.     begin
  452.         if buffersize = 0 then
  453.             buffersize := Default_TCPBufferSize;
  454.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  455.         if connection = nil then
  456.             oe := memFullErr
  457.         else
  458.             with connection^ do begin
  459.                 buffer := NewPtr(buffersize);
  460.                 if buffer = nil then begin
  461.                     oe := memFullErr;
  462.                     DisposPtr(ptr(connection));
  463.                     connection := nil;
  464.                 end
  465.                 else begin
  466.                     magic := MAGICNUMBER;
  467.                     asends := 0;
  468.                     asendcompletes := 0;
  469.                     closedone := false;
  470.                     incomingSize := 0;
  471.                     ZotBlocks;
  472.                     ZeroCB(cb, nil, TCPcsCreate);
  473.                     cb.create.rcvBuff := buffer;
  474.                     cb.create.rcvBuffLen := buffersize;
  475.                     oe := PBControlSync(@cb);
  476.                     stream := cb.tcpStream;
  477.                 end;
  478.             end;
  479.         if (oe <> noErr) and (connection <> nil) then
  480.             DestroyConnection(connection);
  481.         CreateStream := oe;
  482.     end;
  483.  
  484.     function PAOpen (var connection: TCPConnectionPtr; cs: integer; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  485.         var
  486.             oe, ooe: OSErr;
  487.             cbp: MyControlBlockPtr;
  488.             cb: TCPControlBlock;
  489.     begin
  490.         oe := CreateStream(connection, buffersize);
  491.         if oe = noErr then begin
  492.             with connection^ do begin
  493.                 ZotBlocks;
  494.                 oe := GetCB(cbp, connection, cs, userptr, nil);
  495.                 if oe = noErr then begin
  496.                     cbp^.tcp.open.localPort := localPort;
  497.                     cbp^.tcp.open.remoteHost := remoteIP;
  498.                     cbp^.tcp.open.remotePort := remoteport;
  499.                     cbp^.tcp.open.ulpTimeoutAction := -1;
  500.                     oe := MyPBControlAsync(cbp);
  501.                 end;
  502.                 if oe <> noErr then begin
  503.                     ZeroCB(cb, stream, TCPcsRelease);
  504.                     ooe := PBControlSync(@cb);
  505.                     DestroyConnection(connection);
  506.                 end;
  507.             end;
  508.         end;
  509.         SetUserPtr(userptr, oe);
  510.         PAOpen := oe;
  511.     end;
  512.  
  513. { Open a connection to another machine }
  514.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  515.     begin
  516.         TCPActiveOpen := PAOpen(connection, TCPcsActiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  517.     end;
  518.  
  519. { Open a socket on this machine, to wait for a connection }
  520.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  521.     begin
  522.         TCPPassiveOpen := PAOpen(connection, TCPcsPassiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  523.     end;
  524.  
  525.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  526. { Return readCount characters from the TCP connection. }
  527. { WARNING: Doesnt handle incoming buffer, so don't use with TCPReceiveUptp or ReadByte }
  528.         var
  529.             cb: TCPControlBlock;
  530.             oe: OSErr;
  531.     begin
  532.         oe := noErr;
  533.         while (oe = noErr) & (readCount > 0) do begin
  534.             ZotBlocks;
  535.             ZeroCB(cb, connection^.stream, TCPcsRcv);
  536.             cb.receive.rcvBuff := returnPtr;
  537.             cb.receive.rcvBuffLength := readCount;
  538.             oe := PBControlSync(@cb);
  539.             longInt(returnPtr) := longInt(returnPtr) + cb.receive.rcvBuffLength;
  540.             readCount := readCount - cb.receive.rcvBuffLength;
  541.         end;
  542.         TCPRawReceiveChars := oe;
  543.     end;
  544.  
  545. { Return readCount characters from the TCP connection.}
  546.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  547.         var
  548.             readCountStr: Str255;
  549.             l: longInt;
  550.             p: Ptr;
  551.             oe: OSErr;
  552.             cb: TCPControlBlock;
  553.     begin
  554.         oe := ValidateConnection(connection);
  555.         if oe = noErr then
  556.             if readCount < 0 then begin
  557.                 oe := invalidLengthErr;
  558.             end
  559.             else if readCount > 0 then begin
  560.                 p := returnPtr;
  561.                 with connection^ do
  562.                     if incomingSize > 0 then begin
  563.             { Read as much as there is or as much as we need, whichever is less. }
  564.                         if readCount < incomingSize then
  565.                             l := readCount
  566.                         else
  567.                             l := incomingSize;
  568.                         BlockMove(incomingPtr, p, l);
  569.                         incomingPtr := Ptr(ord4(incomingPtr) + l);
  570.                         incomingSize := incomingSize - l;
  571.                         p := Ptr(ord4(p) + l);
  572.                         readCount := readCount - l;
  573.                     end;
  574.                 { If there's more needed, then read it from the connection. }
  575.                 if readCount > 0 then begin
  576.                         { Issue a read and wait until it all arrives). }
  577.                     oe := TCPRawReceiveChars(connection, p, readCount);
  578.                 end;
  579.             end;
  580.         TCPReceiveChars := oe;
  581.     end;
  582.  
  583.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  584.         { Return the next byte in the buffer, reading more in if necessary. }
  585.         var
  586.             waitUntil: longInt;
  587.             readIn: longInt;
  588.             oe: OSErr;
  589.             cb: TCPControlBlock;
  590.     begin
  591.         oe := ValidateConnection(connection);
  592.         if oe = noErr then
  593.             with connection^ do begin            { Check if we need to read in more bytes. }
  594.                 if incomingSize = 0 then begin
  595.                     if (timeout = 0) and (TCPCharsAvailable(connection) = 0) then begin
  596.                         oe := commandTimeoutErr;
  597.                     end
  598.                     else begin
  599.                         waitUntil := TickCount + timeout;
  600.     { keep on trying to read until we get at least one, or the time-out happens. }
  601.                         while (oe = noErr) and (incomingSize = 0) do begin                { Get the status. }
  602.                             readIn := TCPCharsAvailable(connection);    { If there's something there to read, do so. }
  603.                             if readIn > 0 then begin    { Don't read any more than will fit in the buffer. }
  604.                                 if readIn > INCOMINGBUFSIZE then
  605.                                     readIn := INCOMINGBUFSIZE;
  606.                         { Issue the read. }
  607.                                 oe := TCPRawReceiveChars(connection, @inBuf, readIn);
  608.                                 if oe = noErr then begin
  609.                                     incomingSize := readIn;
  610.                                     incomingPtr := @inBuf;
  611.                                 end;
  612.                             end        { If not, do another round or get out, depending on the timeout condition. }
  613.                             else if TickCount > waitUntil then begin
  614.                                 oe := commandTimeOutErr;
  615.                             end;
  616.                         end;
  617.                     end;
  618.                 end;
  619.                 { Get the byte to return. }
  620.                 if incomingSize > 0 then begin
  621.                     b := incomingPtr^;
  622.                     incomingPtr := Ptr(ord4(incomingPtr) + 1);
  623.                     incomingSize := incomingSize - 1;
  624.                 end
  625.                 else
  626.                     b := 0;
  627.             end;
  628.         TCPReadByte := oe;
  629.     end;
  630.  
  631. { Pass in a block of memory (readPtr,readSize), already containing readPos bytes}
  632. { TCPReceiveUpTo will then read characters until a termChar character is reached,}
  633. { or until waitForChars ticks go by without receiving any bytes.  If waitForChars is}
  634. { zero, then TCPReceiveUpTo will return immediately.  If termChar=0, then it}
  635. { will read the entire buffer, and any characters that arrive before a timeout }
  636.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  637.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  638.                                     var gottermchar: boolean): OSErr;
  639.         var
  640.             oe: OSErr;
  641.             inChar: SignedByte;
  642.             p: Ptr;
  643.     begin
  644.         oe := ValidateConnection(connection);
  645.         gottermchar := false;
  646.         if oe = noErr then begin
  647. { Cycle until the timeout happens or we see the termintor character or we run out of room. }
  648.             while (oe = noErr) and (readPos < readSize) and not gottermchar do begin            { Get the next character. }
  649.                 oe := TCPReadByte(connection, charTimeOut, inChar);                    { Ignore the character if it's a zero. }
  650.                 if (oe = noErr) and (inChar <> 0) then begin            { Put it in the result. }
  651.                     p := Ptr(ord4(readPtr) + readPos);
  652.                     p^ := inChar;
  653.                     readPos := readPos + 1;
  654.                     gottermchar := inChar = termChar;
  655.                 end;
  656.             end;
  657.             if oe = commandTimeOutErr then
  658.                 oe := noErr;
  659.         end;
  660.         TCPReceiveUpTo := oe;
  661.     end;
  662.  
  663.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean): OSErr;
  664.         var
  665.             wds: wdsType;
  666.             oe: OSErr;
  667.             cb: TCPControlBlock;
  668.             p: ptr;
  669.     begin
  670.         oe := ValidateConnection(connection);
  671.         if oe = nOErr then
  672.             if writeCount > 0 then begin
  673.                 wds.buffer := writePtr;
  674.                 wds.size := writeCount;
  675.                 wds.term := 0;
  676.                 ZotBlocks;
  677.                 ZeroCB(cb, connection^.stream, TCPcsSend);
  678.                 cb.send.wds := @wds;
  679.                 cb.send.pushFlag := ord(push);
  680.                 oe := PBControlSync(@cb);
  681.             end
  682.             else if writeCount < 0 then
  683.                 oe := InvalidLengthErr;
  684.         TCPSend := oe;
  685.     end;
  686.  
  687. {$PUSH}
  688. {$D-}
  689.     procedure TCPSendComplete (cbp: MyControlBlockPtr);
  690.         var
  691.             oe: OSErr;
  692.     begin
  693.         AddBlock(cbp^.tcp.send.wds);
  694.         with cbp^.tcpc^ do begin
  695.             asendcompletes := asendcompletes + 1;
  696.             if (asendcompletes = asends) and closedone then begin
  697.                 oe := GetCB(cbp, cbp^.tcpc, TCPcsClose, closeuserptr, nil);
  698. { GetCB won't NewPtr because the completion has just released a block }
  699.                 if oe = noErr then begin
  700.                     oe := MyPBControlAsync(cbp);
  701.                 end;
  702.             end;
  703.         end;
  704.     end;
  705. {$POP}
  706.  
  707.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  708.         type
  709.             myblock = record
  710.                     wds: wdsType;
  711.                     data: array[0..100] of byte;
  712.                 end;
  713.             myblockptr = ^myblock;
  714.         var
  715.             oe: OSErr;
  716.             cbp: MyControlBlockPtr;
  717.             p: myblockptr;
  718.     begin
  719.         oe := ValidateConnection(connection);
  720.         if oe = nOErr then
  721.             if writeCount > 0 then begin
  722.                 p := myblockptr(NewPtr(writeCount + SizeOf(wdsType)));
  723.                 if p = nil then
  724.                     oe := memFullErr
  725.                 else begin
  726.                     p^.wds.buffer := @p^.data;
  727.                     p^.wds.size := writeCount;
  728.                     p^.wds.term := 0;
  729.                     with p^.wds do
  730.                         BlockMove(writePtr, buffer, size);
  731.                     oe := GetCB(cbp, connection, TCPcsSend, userptr, @TCPSendComplete);
  732.                     cbp^.tcp.send.wds := POINTER(p);
  733.                     cbp^.tcp.send.pushFlag := ord(push);
  734.                     with connection^ do
  735.                         asends := asends + 1;
  736.                     oe := MyPBControlAsync(cbp);
  737.                     if oe <> noErr then
  738.                         DisposPtr(ptr(p));
  739.                 end;
  740.             end
  741.             else if writeCount < 0 then
  742.                 oe := InvalidLengthErr;
  743.         TCPSendAsync := oe;
  744.     end;
  745.  
  746.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  747.         var
  748.             oe: OSErr;
  749.             cbp: MyControlBlockPtr;
  750.     begin
  751.         oe := ValidateConnection(connection);
  752.         if oe = noErr then
  753.             with connection^ do begin
  754.                 closeuserptr := userptr;
  755.                 if userptr <> nil then
  756.                     userptr^ := inProgress;
  757.                 closedone := true;
  758.                 if asends = asendcompletes then begin
  759.                     ZotBlocks;
  760.                     oe := GetCB(cbp, connection, TCPcsClose, userptr, nil);
  761.                     if oe = noErr then begin
  762.                         oe := MyPBControlAsync(cbp);
  763.                     end;
  764.                 end;
  765.             end;
  766.         SetUserPtr(userptr, oe);
  767.         TCPClose := oe;
  768.     end;
  769.  
  770.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  771.         var
  772.             oe: OSErr;
  773.             cb: TCPControlBlock;
  774.     begin
  775.         oe := ValidateConnection(connection);
  776.         if oe = noErr then begin
  777.             ZotBlocks;
  778.             ZeroCB(cb, connection^.stream, TCPcsAbort);
  779.             oe := PBControlSync(@cb);
  780.         end;
  781.         TCPAbort := oe;
  782.     end;
  783.  
  784. { Release the TCP stream, including the buffer.}
  785.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  786.         var
  787.             oe: OSErr;
  788.             cb: TCPControlBlock;
  789.     begin
  790.         oe := noErr;
  791.         oe := ValidateConnection(connection);
  792.         if oe = noErr then begin
  793.             ZotBlocks;
  794.             ZeroCB(cb, connection^.stream, TCPcsRelease);
  795.             oe := PBControlSync(@cb);
  796.             DestroyConnection(connection);
  797.         end;
  798.         TCPRelease := oe;
  799.     end;
  800.  
  801. {    TCPRawState(connectionID) -- Return the state of the TCP connection.}
  802.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  803.         var
  804.             cb: TCPControlBlock;
  805.             oe: OSErr;
  806.     begin
  807.         localhost := 0;
  808.         localport := 0;
  809.         remotehost := 0;
  810.         remoteport := 0;
  811.         available := 0;
  812.  
  813.         oe := ValidateConnection(connection);
  814.         if oe <> noErr then begin
  815.             state := 99; { Error -> Closed }
  816.         end
  817.         else begin
  818.             ZotBlocks;
  819.             ZeroCB(cb, connection^.stream, TCPcsStatus);
  820.             if PBControlSync(@cb) <> noErr then begin
  821.                 state := 99; { Closed }
  822.             end
  823.             else begin
  824.                 state := cb.status.connectionState;
  825.                 connection^.laststate := state;
  826.                 localhost := cb.status.localhost;
  827.                 localport := cb.status.localport;
  828.                 remotehost := cb.status.remotehost;
  829.                 remoteport := cb.status.remoteport;
  830.                 available := cb.status.amtUnreadData + connection^.incomingSize;
  831.             end;
  832.         end;
  833.     end;
  834.  
  835. { Return the state of the TCP connection.}
  836.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  837.         var
  838.             state: integer;
  839.             localhost: longInt;
  840.             localport: integer;
  841.             remotehost: longInt;
  842.             remoteport: integer;
  843.             available: longInt;
  844.     begin
  845.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  846.         case state of
  847.             0: 
  848.                 TCPState := T_Closed;
  849.             2: 
  850.                 TCPState := T_Listening;
  851.             4, 6: 
  852.                 TCPState := T_Opening;
  853.             8: 
  854.                 TCPState := T_Established;
  855.             10, 12, 16, 18, 20: 
  856.                 TCPState := T_Closing;
  857.             14: 
  858.                 TCPState := T_PleaseClose;
  859.             98: 
  860.                 TCPState := T_WaitingForOpen;
  861.             99: 
  862.                 TCPState := T_Closed;
  863.             otherwise
  864.                 TCPState := T_Unknown;
  865.         end;
  866.     end;
  867.  
  868. {    Return the number of characters available for reading from the TCP connection.}
  869.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  870.         var
  871.             state: integer;
  872.             localhost: longInt;
  873.             localport: integer;
  874.             remotehost: longInt;
  875.             remoteport: integer;
  876.             available: longInt;
  877.     begin
  878.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  879.         TCPCharsAvailable := available;
  880.     end;
  881.  
  882.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  883.         var
  884.             state: integer;
  885.             localhost: longInt;
  886.             localport: integer;
  887.             remotehost: longInt;
  888.             remoteport: integer;
  889.             available: longInt;
  890.     begin
  891.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  892.         TCPLocalPort := localport;
  893.     end;
  894.  
  895.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  896.         var
  897.             buffer: array[0..255] of signedByte;
  898.             f: longInt;
  899.             oe: OSErr;
  900.     begin
  901.         f := TCPCharsAvailable(connection);
  902.         oe := noErr;
  903.         while (f > 0) and (oe = noErr) do begin
  904.             if f > 256 then
  905.                 f := 256;
  906.             oe := TCPReceiveChars(connection, @buffer, f);
  907.             if oe = noErr then
  908.                 f := TCPCharsAvailable(connection);
  909.         end;
  910.         TCPFlush := oe;
  911.     end;
  912.  
  913.     procedure UDPZeroCB (var cb: UDPControlBlock; stream: StreamPtr; call: integer);
  914.     { Zero out the control block parameters. }
  915.         var
  916.             i: integer;
  917.             p: longInt;
  918.     begin
  919.         for p := longInt(@cb) to longInt(@cb) + SizeOf(UDPControlBlock) - 1 do
  920.             ptr(p)^ := 0;
  921.         cb.udpStream := stream;
  922.         cb.ioCRefNum := driver_refnum;
  923.         cb.csCode := call;
  924.     end;
  925.  
  926.     procedure MyNotify (stream: streamPtr; eventCode: integer; connection: UDPConnectionPtr; icmpMsg: ptr);
  927.     begin
  928.         if eventCode = UDPDataArrival then
  929.             if connection^.magic = UDPMagic then
  930.                 connection^.outstanding := connection^.outstanding + 1;
  931.     end;
  932.  
  933.     function UDPCreate (var connection: UDPConnectionPtr; buffer_size: longInt; var localport: integer): OSErr;
  934.         var
  935.             oe: OSErr;
  936.             cb: UDPControlBlock;
  937.     begin
  938.         if buffer_size = 0 then
  939.             buffer_size := Default_UDPBufferSize;
  940.         connection := UDPConnectionPtr(NewPtr(SizeOf(UDPConnectionRecord) + buffer_size));
  941.         oe := MemError;
  942.         if connection <> nil then begin
  943.             connection^.magic := UDPMagic;
  944.             UDPZeroCB(cb, nil, UDPcsCreate);
  945.             cb.create.rcvBuff := ptr(longInt(connection) + SizeOf(UDPConnectionRecord));
  946.             cb.create.rcvBuffLen := buffer_size;
  947.             cb.create.notifyProc := @MyNotify;
  948.             cb.create.userDataPtr := ptr(connection);
  949.             cb.create.localport := localport;
  950.             oe := PBControlSync(@cb);
  951.             localport := cb.create.localport;
  952.             connection^.stream := cb.udpStream;
  953.             connection^.outstanding := 0;
  954.         end;
  955.         UDPCreate := oe;
  956.     end;
  957.  
  958.     function UDPRead (connection: UDPConnectionPtr; timeout: integer; var remoteIP: longInt; var remoteport: integer;{}
  959.                                     var datap: ptr; var datalen: integer): OSErr;
  960.         var
  961.             oe: OSErr;
  962.             cb: UDPControlBlock;
  963.     begin
  964.         UDPZeroCB(cb, connection^.stream, UDPcsRead);
  965.         cb.receive.timeout := timeout;
  966.         oe := PBControlSync(@cb);
  967.         if oe = noErr then
  968.             connection^.outstanding := connection^.outstanding - 1;
  969.         remoteIP := cb.receive.remoteIP;
  970.         remoteport := cb.receive.remoteport;
  971.         datap := cb.receive.rcvBuff;
  972.         datalen := cb.receive.rcvBuffLen;
  973.         UDPRead := oe;
  974.     end;
  975.  
  976.     function UDPReturnBuffer (connection: UDPConnectionPtr; datap: ptr): OSErr;
  977.         var
  978.             oe: OSErr;
  979.             cb: UDPControlBlock;
  980.     begin
  981.         UDPZeroCB(cb, connection^.stream, UDPcsBfrReturn);
  982.         cb.return.rcvBuff := datap;
  983.         oe := PBControlSync(@cb);
  984.         UDPReturnBuffer := oe;
  985.     end;
  986.  
  987.     function UDPDatagramsAvailable (connection: UDPConnectionPtr): integer;
  988.     begin
  989.         UDPDatagramsAvailable := connection^.outstanding;
  990.     end;
  991.  
  992.     function UDPWrite (connection: UDPConnectionPtr; remoteIP: longInt; remoteport: integer;{}
  993.                                     datap: ptr; datalen: integer; checksum: boolean): OSErr;
  994.         var
  995.             oe: OSErr;
  996.             cb: UDPControlBlock;
  997.             wds: wdsType;
  998.     begin
  999.         UDPZeroCB(cb, connection^.stream, UDPcsWrite);
  1000.         cb.send.remoteIP := remoteIP;
  1001.         cb.send.remotePort := remoteport;
  1002.         wds.size := datalen;
  1003.         wds.buffer := datap;
  1004.         wds.term := 0;
  1005.         cb.send.wds := @wds;
  1006.         cb.send.checksum := ord(checksum);
  1007.         oe := PBControlSync(@cb);
  1008.         UDPWrite := oe;
  1009.     end;
  1010.  
  1011.     function UDPRelease (var connection: UDPConnectionPtr): OSErr;
  1012.         var
  1013.             oe: OSErr;
  1014.             cb: UDPControlBlock;
  1015.     begin
  1016.         UDPZeroCB(cb, connection^.stream, UDPcsRelease);
  1017.         oe := PBControlSync(@cb);
  1018.         connection^.magic := UDPBad;
  1019.         DisposPtr(ptr(connection));
  1020.         UDPRelease := oe;
  1021.     end;
  1022.  
  1023.     function UDPMTU (remoteIP: longInt; var mtu: integer): OSErr;
  1024.         var
  1025.             oe: OSErr;
  1026.             cb: UDPControlBlock;
  1027.     begin
  1028.         UDPZeroCB(cb, nil, UDPcsMaxMTUSize);
  1029.         cb.mtu.remoteIP := remoteIP;
  1030.         oe := PBControlSync(@cb);
  1031.         mtu := cb.mtu.mtuSize;
  1032.         UDPMTU := oe;
  1033.     end;
  1034.  
  1035.     procedure IPZeroCB (var cb: IPControlBlock; call: integer);
  1036.     { Zero out the control block parameters. }
  1037.         var
  1038.             i: integer;
  1039.             p: longInt;
  1040.     begin
  1041.         for p := longInt(@cb) to longInt(@cb) + SizeOf(cb) - 1 do
  1042.             ptr(p)^ := 0;
  1043.         cb.ioCRefNum := driver_refnum;
  1044.         cb.csCode := call;
  1045.     end;
  1046.  
  1047.     procedure IPCallCompletion (cbp: IPControlBlockPtr; userdata, extradata: ptr; addr: procPtr);
  1048.     inline
  1049.         $205F, $4E90;
  1050.  
  1051. {$PUSH}
  1052. {$D-}
  1053.     procedure IPICMPCompletionPascal (cbp: IPControlBlockPtr);
  1054.         var
  1055.             index: integer;
  1056.     begin
  1057.         icmp_got_back := icmp_got_back + 1;
  1058.         with cbp^.echoinfo do begin
  1059.             index := ord(userDataPtr);
  1060.             if (index > 0) & (icmp_data_array[index].complete <> nil) then begin
  1061.                 IPCallCompletion(cbp, icmp_data_array[index].userdata, icmp_data_array[index].extradata, icmp_data_array[index].complete);
  1062.                 icmp_data_array[index].complete := nil;
  1063.             end;
  1064.         end;
  1065.     end;
  1066.  
  1067. {$IFC undefined THINK_Pascal}
  1068.  
  1069.     procedure IPICMPCompletion;
  1070.         asm;
  1071.     begin
  1072.         move.l 4 (sp), a0
  1073.         move.l a0,-(sp)
  1074.         jsr IPICMPCompletionPascal
  1075.         rts
  1076. {        move.l 4 (sp), a0}
  1077. {        move.l a0,-(sp)}
  1078. {        jsr IPICMPCompletionPascal}
  1079. {        rts}
  1080.     end;
  1081.  
  1082. {$ELSEC}
  1083.  
  1084.     procedure IPICMPCompletion;
  1085.     begin
  1086.         IPICMPCompletionPascal(IPControlBlockPtr(GetStackFrame^.paramblockptr));
  1087.     end;
  1088.  
  1089. {$ENDC}
  1090.  
  1091.  
  1092. {$POP}
  1093.  
  1094.     function IPSendICMPEcho (remotehost: ipAddr; timeout: integer; datap: ptr; datalen: integer; complete: ProcPtr; userdata: univ ptr; extradata: univ ptr): OSErr;
  1095.         var
  1096.             cb: IPControlBlock;
  1097.             i, index: integer;
  1098.             oe: OSErr;
  1099.     begin
  1100.         index := -1;
  1101.         if complete <> nil then begin
  1102.             for i := 1 to max_ICMPDataArray do begin
  1103.                 if icmp_data_array[i].complete = nil then begin
  1104.                     index := i;
  1105.                     icmp_data_array[i].complete := complete;
  1106.                     icmp_data_array[i].userdata := userdata;
  1107.                     icmp_data_array[i].extradata := extradata;
  1108.                     leave;
  1109.                 end;
  1110.             end;
  1111.         end;
  1112.         IPZeroCB(cb, TCPcsEchoICMP);
  1113.         cb.echo.dest := remotehost;
  1114.         cb.echo.data.buffer := datap;
  1115.         cb.echo.data.size := datalen;
  1116.         cb.echo.timeout := timeout;
  1117.         cb.echo.options := nil;
  1118.         cb.echo.optlength := 0;
  1119.         cb.echo.icmpCompletion := @IPICMPCompletion;
  1120.         cb.echo.userDataPtr := ptr(ord(index)); { Avoid tickling MW bug }
  1121.         oe := PBControlSync(@cb);
  1122.         if oe = noErr then
  1123.             icmp_sent_out := icmp_sent_out + 1;
  1124.         IPSendICMPEcho := oe;
  1125.     end;
  1126.  
  1127.     function IPGetMyIPAddr (var myIP: longInt): OSErr;
  1128.         var
  1129.             cb: IPControlBlock;
  1130.             oe: OSErr;
  1131.     begin
  1132.         IPZeroCB(cb, TCPcsGetMyIP);
  1133.         oe := PBControlSync(@cb);
  1134.         myIP := cb.getmyip.ourAddress;
  1135.         IPGetMyIPAddr := oe;
  1136.     end;
  1137.  
  1138.     procedure CallDNRCompletion (drp: DNRRecordPtr; proc: ProcPtr);
  1139.     inline
  1140.         $205F, $4E90;
  1141.  
  1142. {$PUSH}
  1143. {$D-}
  1144.     procedure DNRNameToAddrCompletionProc (hip: hostInfoPtr; drp: DNRRecordPtr);
  1145.     begin
  1146.         drp^.ioResult := hip^.rtnCode;
  1147.         drp^.addr := drp^.hi.addrs[1];
  1148.         if drp^.completion <> nil then begin
  1149.             CallDNRCompletion(drp, drp^.completion);
  1150.         end;
  1151.     end;
  1152. {$POP}
  1153.  
  1154.     procedure DNRNameToAddr (name: Str255; drp: DNRRecordPtr; completion: DNRCompletionProcPtr);
  1155.         var
  1156.             err: OSErr;
  1157.     begin
  1158.         drp^.ioResult := 1;
  1159.         drp^.name := name;
  1160.         drp^.completion := completion;
  1161.         err := StrToAddr(name, drp^.hi, @DNRNameToAddrCompletionProc, ptr(drp));
  1162.         if err <> cacheFaultErr then begin
  1163.             drp^.hi.rtnCode := err;
  1164.             DNRNameToAddrCompletionProc(@drp^.hi, drp);
  1165.         end;
  1166.     end;
  1167.  
  1168. {$PUSH}
  1169. {$D-}
  1170.     procedure DNRAddrToNameCompletionProc (hip: hostInfoPtr; drp: DNRRecordPtr);
  1171.     begin
  1172.         drp^.ioResult := hip^.rtnCode;
  1173.         if drp^.ioResult = noErr then begin
  1174.             BlockMove(@hip^.rtnHostName, @drp^.name, SizeOf(drp^.name));
  1175.             SanitizeHostName(drp^.name);
  1176.         end;
  1177. {    DebugStr(StringOf('DNRAddrToNameCompletionProc ', drp^.ioResult, ' ', drp^.name, ';g'));}
  1178.         if drp^.completion <> nil then begin
  1179.             CallDNRCompletion(drp, drp^.completion);
  1180.         end;
  1181.     end;
  1182. {$POP}
  1183.  
  1184.     procedure DNRAddrToName (addr: longInt; drp: DNRRecordPtr; completion: DNRCompletionProcPtr);
  1185.         var
  1186.             err: OSErr;
  1187.     begin
  1188.         drp^.ioResult := 1;
  1189.         drp^.addr := addr;
  1190.         drp^.completion := completion;
  1191.         AddrToStr(addr, drp^.name);
  1192.         err := AddrToName(addr, drp^.hi, @DNRAddrToNameCompletionProc, ptr(drp));
  1193. {    DebugStr(StringOf('DNRAddrToName ', err, ';g'));}
  1194.         if err <> cacheFaultErr then begin
  1195.             drp^.hi.rtnCode := err;
  1196.             DNRAddrToNameCompletionProc(@drp^.hi, drp);
  1197.         end;
  1198.     end;
  1199.  
  1200. end.
  1201. dnrptr: ptr;
  1202. function TCPNameToAddr (var hostName: str255; timeout: longInt): longInt;
  1203.     function TCPOpenResolver (var dataptr: ptr): OSErr;
  1204.         function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  1205.             procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  1206.                 function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  1207.                     procedure TCPCloseResolver (dataptr: ptr);
  1208.                         function TCPCachedStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  1209.                             procedure TCPSetCache (var hi: hostInfo; host: str255);
  1210.  
  1211.                                 const
  1212.                                     cache_count = 3;
  1213.                                 var
  1214.                                     caches: array[1..cache_count] of hostInfo;
  1215.                                     next_cache: integer;
  1216.  
  1217.                                 function TCPCachedStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  1218.                                     var
  1219.                                         i: integer;
  1220.                                 begin
  1221.                                     TCPCachedStrToAddr := noErr;
  1222.                                     done := 0;
  1223.                                     for i := 1 to cache_count do begin
  1224.                                         if (caches[i].rtnCode = noErr) & (IUEqualString(hostName, caches[i].rtnHostName) = 0) then begin
  1225.                                             done := 1;
  1226.                                             rtnStruct := caches[i];
  1227.                                             next_cache := i;
  1228.                                             leave;
  1229.                                         end;
  1230.                                     end;
  1231.                                     if done = 0 then begin
  1232.                                         TCPCachedStrToAddr := TCPStrToAddr(dataptr, hostName, rtnStruct, done);
  1233.                                     end;
  1234.                                 end;
  1235.  
  1236.                                 procedure TCPSetCache (var hi: hostInfo; host: str255);
  1237.                                 begin
  1238.                                     if hi.rtnCode = noErr then begin
  1239.                                         caches[next_cache] := hi;
  1240.                                         caches[next_cache].rtnHostName := host;
  1241.                                         next_cache := next_cache mod cache_count + 1;
  1242.                                     end;
  1243.                                 end;
  1244.                                 for i := 1 to cache_count do begin
  1245.                                     caches[i].rtnCode := -1;
  1246.                                 end;
  1247.                                 next_cache := 1;